/*-----------------------------------------------------------------------------
 * @file   ring_buf.h
 * @brief  thread safe ring buffer, NOT support dynamic expanded memory
 * @author WLH<wanglehui8357@163.com> Copyright (C) 2021~2099.
 ----------------------------------------------------------------------------*/
#ifndef RING_BUFFER_H
#define RING_BUFFER_H

#include "globaldef.h"

BEGIN_DECLS

/** @brief Ring Buffer Management Structure.
 *
 *  Type define for a new ring buffer object. Buffers should be initialized via a call to
 *  \ref ringbuf_create() before use.
 */
typedef struct
{
    U8* data_ptr; /* Current storage location in the circular buffer.            */
    U32 capacity; /* Size of the buffer's underlying storage array.              */
    vU32 start;   /* Index of the start of the buffer's underlying storage array */
    vU32 end;     /* Index of the end of the buffer's underlying storage array.  */
} ring_buf_t;

/*-----------------------------------------------------------------------------
 * public functions
 ----------------------------------------------------------------------------*/
/** @brief Initializes a ring buffer ready for use. Buffers must be initialized via this function
 *  before any operations are called upon them. Already initialized buffers may be reset
 *  by re-initializing them using this function.
 *
 *  @param[out] buffer   Pointer to a ring buffer structure to initialize.
 *  @param[out] data_ptr Pointer to a global array that will hold the data stored into the ring buffer.
 *  @param[out] size     Maximum number of bytes that can be stored in the underlying data array.
 *
 *  NOTE WELL: NO CHECKING THAT THE POINTERS OR SIZE ARE CORRECT !!!
 */
ring_buf_t* ringbuf_create(U8* data_ptr, U32 size);

/** @brief Flushes the contents of a ring buffer.
 *
 *  @param[out] buffer   Pointer to a ring buffer structure to flush out.
 */
void ringbuf_clear(ring_buf_t* const buffer);

/** @brief Retrieves the current number of bytes stored in a particular buffer. This value is computed
 *  by entering an atomic lock on the buffer, so that the buffer cannot be modified while the
 *  computation takes place. This value should be cached when reading out the contents of the buffer,
 *  so that as small a time as possible is spent in an atomic lock.
 *
 *  @note The value returned by this function is guaranteed to only be the minimum number of bytes
 *        stored in the given buffer; this value may change as other threads write new data, thus
 *        the returned number should be used only to determine how many successive reads may safely
 *        be performed on the buffer.
 *
 *  @param[in] buffer  Pointer to a ring buffer structure whose count is to be computed.
 *  @return Number of bytes currently stored in the buffer.
 */
U32 ringbuf_used(ring_buf_t* const buffer);

/** @brief Retrieves the free space in a particular buffer. This value is computed by entering an atomic lock
 *  on the buffer, so that the buffer cannot be modified while the computation takes place.
 *
 *  @note The value returned by this function is guaranteed to only be the maximum number of bytes
 *        free in the given buffer; this value may change as other threads write new data, thus
 *        the returned number should be used only to determine how many successive writes may safely
 *        be performed on the buffer when there is a single writer thread.
 *
 *  @param[in] buffer  Pointer to a ring buffer structure whose free count is to be computed.
 *  @return Number of free bytes in the buffer.
 */
U32 ringbuf_unused(ring_buf_t* const buffer);

/** @brief Atomically determines if the specified ring buffer contains any data. This should
 *  be tested before removing data from the buffer, to ensure that the buffer does not
 *  underflow.
 *
 *  If the data is to be removed in a loop, store the total number of bytes stored in the
 *  buffer (via a call to the \ref ringbuf_used() function) in a temporary variable
 *  to reduce the time spent in atomicity locks.
 *
 *  @param[in,out] buffer  Pointer to a ring buffer structure to check.
 *  @return bool \c true if the buffer contains no free space, \c false otherwise.
 */
bool ringbuf_is_empty(ring_buf_t* const buffer);

/** @brief Atomically determines if the specified ring buffer contains any free space. This should
 *  be tested before storing data to the buffer, to ensure that no data is lost due to a buffer overrun.
 *
 *  @param[in,out] buffer  Pointer to a ring buffer structure to check.
 *  @return bool \c true if the buffer contains no free space, \c false otherwise.
 */
bool ringbuf_is_full(ring_buf_t* const buffer);

/** @brief Inserts an element into the ring buffer.
 *
 *  @warning Only one execution thread (main program thread or an ISR) may insert into a single buffer
 *           otherwise data corruption may occur. Insertion and removal may occur from different execution
 *           threads.
 *
 *  @param[in,out] buffer  Pointer to a ring buffer structure to insert into.
 *  @param[in]     data    Data element to insert into the buffer.
 *  @return bool \c true if the buffer contains no free space, \c false otherwise.
 */
bool ringbuf_putc(ring_buf_t* buffer, const U8 data);

/** @brief Removes an element from the ring buffer.
 *
 *  @warning Only one execution thread (main program thread or an ISR) may remove from a single buffer
 *           otherwise data corruption may occur. Insertion and removal may occur from different execution
 *           threads.
 *
 *  @param[in,out] buffer  Pointer to a ring buffer structure to retrieve from.
 *
 *  @return Next data element stored in the buffer.
 */
U8 ringbuf_getc(ring_buf_t* buffer);

/**
 * @brief      read bytes from RingBuffer
 *
 * @param      buffer pointer to a ring buffer structure
 * @param[in]  target The buffer to save read data
 * @param[in]  amount the size of data to read
 * @return     the actual size to read
 */
U32 ringbuf_read(ring_buf_t* buffer, U8* target, U32 amount);

/**
 * @brief      write bytes to RingBuffer
 *
 * @param      buffer pointer to a ring buffer structure
 * @param[in]  data   The buffer to save write data
 * @param[in]  size   the size of data to write
 * @return     the actual size to write
 */
U32 ringbuf_write(ring_buf_t* buffer, const U8* data, U32 size);

END_DECLS

#endif //~RING_BUFFER_H
